namespace mmmm {

    /**
 * 当我们定义一个变量不确定类型的时候有两种解决方式：
    使用any
    使用any定义时存在的问题：虽然 以 知道传入值的类型但是无法获取函数返回值的类型；另外也失去了ts类型保护的优势
    使用泛型
    泛型指的是在定义函数/接口/类型时，不预先指定具体的类型，而是在使用的时候在指定类型限制的一种特性
    约定俗成的标识符：
    T，泛型类型参数名
    U, V: 通常用于表示第二、第三个泛型类型参数
    K, V: 用于表示键（Key）和值（Value）的泛型类型参数
    E: 用于表示数组元素的泛型类型参数
    R: 用于表示函数返回值的泛型类型参数
*/

/**
 * 泛型使用场景
 */
// new Array()


/***
 * 函数泛型
 * 类似于函数传参，传什么数据类型，T就表示什么数据类型， 使用表示，T也可以换成任意字符串
 */

// function am<T>(t:T):void{
// }
// am(1)




/**
* 
* 类泛型
注意类声明泛型和类内部函数生成泛型
*/
// class Anim {
//     // name: T;
//     // constructor(name: T) {
//     //     this.name = name;
//     // }
//     action<T>(say: T) {
//         console.log(say)
//     }
// }
// let cat = new Anim();
// cat.action(12)

/***
 * 接口泛型
 * 为什么和类不同
 * 1,匿名
 * 2,有名
 */
//  interface Search {
//     <T, Y>(name: T, age: Y): T//匿名
//     }
// let fn: Search = function<T, Y>(name: T, id: Y): T {
//     console.log(name, id)
//     return name;
// }


/***
 * 泛型约束
 * 泛型的类型参数 Type 可以代表任意类型，这会导致变量无法访问任何属性。
 * 就需要为泛型添加约束来收缩类型（收缩类型取值范围）
 * extends
 */

 interface Person2 {
    name: string;
    age: number;
}
function student4<T extends Person2>(arg: T): T {
   
    return arg;
}

// class VBV {
// }

// student4<VBV>({})


/***
 * 泛型调用时候去掉<>情况
 */
/***
 * 泛型与默认值
 * <T = string>
 */
//  class Arr<Type = string> {
//     list: Type[] = [];
// }
// let arr2 = new Arr(); // 未传入类型参数，默认为string类型
// arr2.list = ['a','b']; // 正确
// arr2.list = [1,2,3,4]; // 报错



/***
 * Partial
partial<T>的作用就是将某个类型中的属性全部变为可选项?
 */
interface Person {
    name: string;
    age: number;
}
function student2<T extends Person>(arg: Partial<T>): Partial<T> {
    return arg;
}
student2({ name: 'lili',age:1 });

//============================不常用===================================
/***
 * Record<K extends keyof any, T>的作用是将K中所有的属性转换为T类型
 */
interface PageInfo {
    title: string
}
type Page = 'home' | 'about' | 'other';
const x: Record<Page, PageInfo> = {
    home: { title: "xxx" },
    about: { title: "aaa" },
    other: { title: "ccc" },
};
/***
 * Pick<T, K extends keyof T>的作用是将某个类型中的子属性挑出来，变成包含这个类型部分属性的子类型
 */
interface Todo {
    title: string,
    desc: string,
    time: string
}
type TodoPreview = Pick<Todo, 'title' | 'time'>;
const todo: TodoPreview = {
    title: '吃饭',
    time: '明天',
    // desc:""
}
/***
 * 
 * Exclude<T,U>的作用是将某个类型中属于另一个类型的属性移除掉
 */
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
const t: T0 = 'b';






/***泛型识别 */

class A {
    str: string = 'a'
}
class B extends A {
    str = "b"
}
class C extends A {
    str = "c"
    constructor(str: string) {
        super()
        this.str = str
    }
}

let arr: Array<A> = [new B(), new C("c"), new A()];
let originMenusMap: Map<string, C | null> = new Map<string, C | null>(arr.map<[string, C | null]>(item => [item.str, (item instanceof C) ? item : null]));
// originMenusMap.forEach((value, key) => {
//     console.log(key, typeof value)
// });

function test2<T extends U, U extends number[]>(t: T, u: U): number {
    return t.length + u.length
}






//===============================================================
// https://developer.aliyun.com/article/1069311
/***
 * TS 泛型和泛型约束。首先定义了 T 类型并使用 extends 关键字继承 object 类型的子类型，然后使用 keyof 操作符获取 T 类型的所有键，
 * 它的返回 类型是联合 类型，最后利用 extends 关键字约束 K 类型必须为 keyof T 联合类型的子类型
 */

function prop<T, K extends keyof T>(obj: T, key: K) {
    return obj[key]
}
let o = { a: 1, b: 2, c: 3 }
prop(o, 'a') 

// prop(o, 'd') 
//，我们需要约束一下这个o里面并没有的东西，此时就会报错发现找不到
//通过提示，我门可以看到类型"d"的参数不能赋给类型"a"|"b"|"c"的参数

}


type ReturnData = {}
interface Iview<T extends ReturnData> {
    render(t:T):void
}

interface Idata<V,T extends ReturnData>{
    request(param:V): T;
}

class View1Data implements Idata<string,ReturnData>{
    request(param: string): ReturnData {
        return {a:1}
    }
    
}

class View1 implements Iview<ReturnData> {
    constructor(){
        this.init()
    }
    init(){
        this.render(new View1Data().request(""))
    }
    render(t: ReturnData): void {
        console.log(t)
    }

}



